home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / xvisrc.zip / MOVEMENT.C < prev    next >
C/C++ Source or Header  |  1992-07-28  |  13KB  |  615 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)movement.c    2.2 (Chris & John Downey) 9/1/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     movement.c
  14. * module function:
  15.     Movement of the cursor and the window into the buffer.
  16. * history:
  17.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  18.     Originally by Tim Thompson (twitch!tjt)
  19.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  20.     Heavily modified by Chris & John Downey
  21.  
  22. ***/
  23.  
  24. #include "xvi.h"
  25.  
  26. /*
  27.  * Shift a buffer's contents down relative to its window,
  28.  * but don't change the display.
  29.  *
  30.  * Return number of physical lines shifted.
  31.  */
  32. int
  33. shiftdown(win, nlines)
  34. register Xviwin    *win;
  35. unsigned    nlines;
  36. {
  37.     register unsigned    k;        /* loop counter */
  38.     int            done = 0;    /* # of physical lines done */
  39.  
  40.     for (k = 0; k < nlines; k++) {
  41.     register Line    *p;
  42.     register long    physlines;
  43.  
  44.     /*
  45.      * Set the top screen line to the previous one.
  46.      */
  47.     p = win->w_topline->l_prev;
  48.     if (p == win->w_buffer->b_line0)
  49.         break;
  50.  
  51.     physlines = plines(win, p);
  52.     done += LONG2INT(physlines);
  53.  
  54.     win->w_topline = p;
  55.     }
  56.  
  57.     win->w_curs_new = TRUE;
  58.  
  59.     return(done);
  60. }
  61.  
  62. /*
  63.  * Shift a buffer's contents up relative to its window,
  64.  * but don't change the display.
  65.  *
  66.  * Return number of physical lines shifted.
  67.  */
  68. int
  69. shiftup(win, nlines)
  70. register Xviwin    *win;
  71. unsigned    nlines;
  72. {
  73.     register unsigned    k;        /* loop counter */
  74.     int            done = 0;    /* # of physical lines done */
  75.  
  76.     for (k = 0; k < nlines; k++) {
  77.     register Line    *p;
  78.     register long    physlines;
  79.  
  80.     /*
  81.      * Set the top screen line to the next one.
  82.      */
  83.     p = win->w_topline->l_next;
  84.     if (p == win->w_buffer->b_lastline)
  85.         break;
  86.  
  87.     physlines = plines(win, win->w_topline);
  88.     done += LONG2INT(physlines);
  89.  
  90.     win->w_topline = p;
  91.     }
  92.  
  93.     win->w_curs_new = TRUE;
  94.  
  95.     return(done);
  96. }
  97.  
  98. /*
  99.  * Scroll the screen down 'nlines' lines.
  100.  */
  101. void
  102. scrolldown(win, nlines)
  103. register Xviwin    *win;
  104. unsigned    nlines;
  105. {
  106.     s_ins(win, 0, shiftdown(win, nlines));
  107. }
  108.  
  109. /*
  110.  * Scroll the screen up 'nlines' lines.
  111.  */
  112. void
  113. scrollup(win, nlines)
  114. register Xviwin    *win;
  115. unsigned    nlines;
  116. {
  117.     s_del(win, 0, shiftup(win, nlines));
  118. }
  119.  
  120. /*
  121.  * oneup
  122.  * onedown
  123.  * one_left
  124.  * one_right
  125.  *
  126.  * Move cursor one char {left,right} or one line {up,down}.
  127.  *
  128.  * Return TRUE when successful, FALSE when we hit a boundary
  129.  * (of a line, or the file).
  130.  */
  131.  
  132. /*
  133.  * Move the cursor up 'nlines' lines.
  134.  *
  135.  * Returns TRUE if we moved at all.
  136.  */
  137. bool_t
  138. oneup(win, nlines)
  139. register Xviwin    *win;
  140. long        nlines;
  141. {
  142.     Posn        *pp;
  143.     register Line    *curr_line;
  144.     long        k;
  145.  
  146.     pp = win->w_cursor;
  147.     curr_line = pp->p_line;
  148.  
  149.     for (k = 0; k < nlines; k++) {
  150.     /*
  151.      * Look for the previous line.
  152.      */
  153.     if (curr_line == win->w_buffer->b_file) {
  154.         /*
  155.          * If we've at least backed up a little ...
  156.          */
  157.         if (k > 0) {
  158.         break;    /* to update the cursor, etc. */
  159.         } else {
  160.         return(FALSE);
  161.         }
  162.     }
  163.     curr_line = curr_line->l_prev;
  164.     }
  165.  
  166.     pp->p_line = curr_line;
  167.     pp->p_index = 0;
  168.     win->w_curs_new = TRUE;
  169.  
  170.     /*
  171.      * Try to advance to the column we want to be at.
  172.      */
  173.     coladvance(win, win->w_curswant);
  174.     return(TRUE);
  175. }
  176.  
  177. /*
  178.  * Move the cursor down 'nlines' lines.
  179.  *
  180.  * Returns TRUE if we moved at all.
  181.  */
  182. bool_t
  183. onedown(win, nlines)
  184. register Xviwin    *win;
  185. long        nlines;
  186. {
  187.     Posn        *pp;
  188.     register Line    *curr_line;
  189.     long        k;
  190.  
  191.     pp = win->w_cursor;
  192.     curr_line = pp->p_line;
  193.  
  194.     for (k = 0; k < nlines; k++) {
  195.     /*
  196.      * Look for the next line.
  197.      */
  198.     if (curr_line->l_next == win->w_buffer->b_lastline) {
  199.         if (k > 0) {
  200.         break;
  201.         } else {
  202.         return(FALSE);
  203.         }
  204.     }
  205.     curr_line = curr_line->l_next;
  206.     }
  207.  
  208.     pp->p_line = curr_line;
  209.     pp->p_index = 0;
  210.     win->w_curs_new = TRUE;
  211.  
  212.     /*
  213.      * Try to advance to the column we want to be at.
  214.      */
  215.     coladvance(win, win->w_curswant);
  216.  
  217.     return(TRUE);
  218. }
  219.  
  220. /*ARGSUSED*/
  221. bool_t
  222. one_left(window, unused)
  223. Xviwin    *window;
  224. bool_t    unused;
  225. {
  226.     Posn    *p;
  227.  
  228.     window->w_set_want_col = TRUE;
  229.     p = window->w_cursor;
  230.  
  231.     if (p->p_index > 0) {
  232.     p->p_index--;
  233.     curs_horiz(window, -1);
  234.     return(TRUE);
  235.     } else {
  236.     return(FALSE);
  237.     }
  238. }
  239.  
  240. /*
  241.  * The move_past_end parameter will be TRUE if moving past the end
  242.  * of the line (onto the first '\0' character) is allowed. We will
  243.  * never move past this character.
  244.  */
  245. bool_t
  246. one_right(window, move_past_end)
  247. Xviwin    *window;
  248. bool_t    move_past_end;
  249. {
  250.     Posn    *p;
  251.     char    *txtp;
  252.  
  253.     window->w_set_want_col = TRUE;
  254.     p = window->w_cursor;
  255.     txtp = &p->p_line->l_text[p->p_index];
  256.  
  257.     if (txtp[0] != '\0' && (move_past_end || txtp[1] != '\0')) {
  258.     p->p_index++;
  259.     curs_horiz(window, 1);
  260.     return(TRUE);
  261.     } else {
  262.     return(FALSE);
  263.     }
  264. }
  265.  
  266. void
  267. begin_line(window, flag)
  268. Xviwin    *window;
  269. bool_t    flag;
  270. {
  271.     register Posn    *pos;
  272.     register int    c;
  273.  
  274.     pos = window->w_cursor;
  275.  
  276.     if (flag) {
  277.     char        *startp;
  278.     register char    *p;
  279.  
  280.     startp = p = pos->p_line->l_text;
  281.     while ((c = *p) != '\0' && p[1] != '\0' && is_space(c)) {
  282.         p++;
  283.     }
  284.     pos->p_index = p - startp;
  285.     } else {
  286.     pos->p_index = 0;
  287.     }
  288.  
  289.     window->w_set_want_col = TRUE;
  290.     window->w_curs_new = TRUE;
  291. }
  292.  
  293. /*
  294.  * coladvance(win, col)
  295.  *
  296.  * Try to advance to the specified column, starting at p.
  297.  */
  298. void
  299. coladvance(win, col)
  300. register Xviwin    *win;
  301. register int    col;
  302. {
  303.     register int    c;
  304.     register char    *tstart, *tp;
  305.  
  306.     tp = tstart = win->w_cursor->p_line->l_text;
  307.     /*
  308.      * Try to advance to the specified column.
  309.      */
  310.     for (c = 0; c < col; ) {
  311.     if (*tp == '\0') {
  312.         /*
  313.          * We're at the end of the line.
  314.          */
  315.         break;
  316.     }
  317.     c += vischar(*tp, (char **) NULL, (Pb(P_list) ? -1 : c));
  318.  
  319.     tp++;
  320.     }
  321.  
  322.     /*
  323.      * Move back onto last character if we have to.
  324.      */
  325.     if ((*tp == '\0' || c > col) && tp > tstart)
  326.     --tp;
  327.     win->w_cursor->p_index = tp - tstart;
  328.     curwin->w_curs_new = TRUE;
  329. }
  330.  
  331. /*
  332.  * Go to the specified line number in the current buffer.
  333.  * Position the cursor at the first non-white.
  334.  */
  335. void
  336. do_goto(line)
  337. long line;
  338. {
  339.     curwin->w_cursor->p_line = gotoline(curbuf, line);
  340.     curwin->w_cursor->p_index = 0;
  341.     curwin->w_curs_new = TRUE;
  342.     begin_line(curwin, TRUE);
  343. }
  344.  
  345. /*
  346.  * Move the cursor to the specified line, at the specified position.
  347.  */
  348. void
  349. move_cursor(win, lp, index)
  350. Xviwin    *win;
  351. Line    *lp;
  352. int    index;
  353. {
  354.     win->w_cursor->p_line = lp;
  355.     win->w_cursor->p_index = index;
  356.     win->w_curs_new = TRUE;
  357. }
  358.  
  359. /*
  360.  * Adjust window so that currline is, as far as possible, in the
  361.  * middle of it.
  362.  *
  363.  * Don't update the screen: move_window_to_cursor() does that.
  364.  */
  365. static void
  366. jump(win, currline, halfwinsize)
  367. Xviwin        *win;
  368. Line        *currline;
  369. int        halfwinsize;
  370. {
  371.     register int    count;
  372.     register int    spare;
  373.     register Line    *topline;
  374.     register Line    *filestart = win->w_buffer->b_file;
  375.  
  376.     spare = win->w_nrows - (unsigned int) plines(win, topline = currline) - 1;
  377.     for (count = 0; count < halfwinsize && topline != filestart;) {
  378.     topline = topline->l_prev;
  379.     count += (unsigned int) plines(win, topline);
  380.     if (count >= spare) {
  381.         if (count > spare)
  382.         topline = topline->l_next;
  383.         break;
  384.     }
  385.     }
  386.     win->w_topline = topline;
  387.     update_buffer(win->w_buffer);
  388.  
  389.     /*
  390.      * The result of calling show_file_info here is that if the
  391.      * cursor moves a long away - e.g. for a "G" command or a search
  392.      * - the status line is updated with the correct line number.
  393.      * This is a small cost compared to updating the whole window.
  394.      */
  395.     show_file_info(win);
  396. }
  397.  
  398. /*
  399.  * Update the position of the window relative to the buffer, moving
  400.  * the window if necessary to ensure that the cursor is inside the
  401.  * window boundary. w_topline, w_botline and w_cursor must be set to
  402.  * something reasonable for us to be able to test whether the cursor
  403.  * is in the window or not, unless (as a special case) the buffer is
  404.  * empty - in this case, the cursor sits at top left, despite there
  405.  * being no character there for it to sit on.
  406.  *
  407.  * If we have to move the window only a small amount, we try to scroll
  408.  * it rather than redrawing. If we have to redraw, we also rewrite the
  409.  * status line on the principle that it's probably a negligible cost
  410.  * compared to updating the whole window.
  411.  */
  412. void
  413. move_window_to_cursor(win)
  414. register Xviwin        *win;
  415. {
  416.     register Line    *currline;
  417.     int            halfwinsize;
  418.     long        distance;
  419.  
  420.     currline = win->w_cursor->p_line;
  421.     halfwinsize = (win->w_nrows - 1) / 2;
  422.  
  423.     /*
  424.      * First stage: move window towards cursor.
  425.      */
  426.     if (bufempty(win->w_buffer)) {
  427.     /*
  428.      * Special case - file is empty.
  429.      */
  430.     win->w_topline = win->w_buffer->b_file;
  431.     win->w_cursor->p_line = win->w_buffer->b_file;
  432.     win->w_cursor->p_index = 0;
  433.     win->w_curs_new = TRUE;
  434.  
  435.     } else if (earlier(currline, win->w_topline)) {
  436.     long    nlines;
  437.  
  438.     /*
  439.      * The cursor is above the top of the window; move the
  440.      * window towards it.
  441.      */
  442.  
  443.     nlines = cntplines(win, currline, win->w_topline);
  444.  
  445.     /*
  446.      * Decide whether it's worthwhile - & possible - to
  447.      * scroll to get the window to the right place.
  448.      *
  449.      * It's possible if we can have scrolling regions, or
  450.      * we can insert screen lines & the window is at the
  451.      * bottom of the screen.
  452.      *
  453.      * The actual scrolling is done by s_ins(), in
  454.      * screen.c.
  455.      *
  456.      * If Pn(P_jumpscroll) is js_OFF, we don't use
  457.      * jumpscroll anyway.
  458.      */
  459.     if (
  460.         nlines > halfwinsize
  461.         ||
  462.         Pn(P_jumpscroll) == js_ON
  463.         ||
  464.         (
  465.         !can_scroll_area
  466.         &&
  467.         Pn(P_jumpscroll) == js_AUTO
  468.         &&
  469.         (
  470.             !can_ins_line
  471.             ||
  472.             win->w_cmdline < (Rows - 1)
  473.         )
  474.         )
  475.     ) {
  476.         jump(win, currline, halfwinsize);
  477.     } else {
  478.         s_ins(win, 0, (int) nlines);
  479.         win->w_topline = currline;
  480.         update_window(win);
  481.     }
  482.     } else {
  483.     long    nlines;
  484.  
  485.     /*
  486.      * The cursor is on or after the last line of the screen,
  487.      * so we might have to move the screen to it. Check to see
  488.      * whether we are actually off the screen; if not, we don't
  489.      * have to do anything. We do this in a certain way so as
  490.      * not to do any unnecessary calculations; this routine is
  491.      * called very often, so we must not take too much time.
  492.      */
  493.     if (earlier(currline, win->w_botline->l_prev) ||
  494.                 (plines(win, currline) == 1 &&
  495.                  earlier(currline, win->w_botline))) {
  496.         return;
  497.     }
  498.     distance = cntplines(win, win->w_topline, currline->l_next);
  499.     if (distance <= win->w_nrows - 1) {
  500.         return;
  501.     }
  502.  
  503.     /*
  504.      * The cursor is off the bottom of the window, or the
  505.      * line the cursor is on won't completely fit in the
  506.      * window.
  507.      */
  508.  
  509.     nlines = distance - (win->w_nrows - 1);
  510.  
  511.     /*
  512.      * Decide whether it's worthwhile - & possible - to
  513.      * scroll to get the window to the right place.
  514.      *
  515.      * It's possible if we can have scrolling regions, or
  516.      * we can delete screen lines & the window is at the
  517.      * bottom of the screen, or it's the only window on
  518.      * the screen.
  519.      *
  520.      * The actual scrolling is done by s_del(), in
  521.      * screen.c.
  522.      *
  523.      * If Pn(P_jumpscroll) is js_OFF, we don't use
  524.      * jumpscroll anyway.
  525.      */
  526.     if (
  527.         nlines > halfwinsize
  528.         ||
  529.         Pn(P_jumpscroll) == js_ON
  530.         ||
  531.         (
  532.         !can_scroll_area
  533.         &&
  534.         Pn(P_jumpscroll) == js_AUTO
  535.         &&
  536.         (
  537.             (
  538.             !can_del_line
  539.             &&
  540.             win->w_winpos != 0
  541.             )
  542.             ||
  543.             win->w_cmdline < (Rows - 1)
  544.         )
  545.         )
  546.     ) {
  547.         jump(win, currline, halfwinsize);
  548.     } else {
  549.         long    done = 0;
  550.         Line    *l;
  551.         Line    *newtopline;
  552.  
  553.         /*
  554.          * Work out where we should place topline in
  555.          * order that the cursor line is made the
  556.          * bottom line of the screen.
  557.          */
  558.         for (l = currline;; l = l->l_prev) {
  559.         done += plines(win, l);
  560.         if (done >= win->w_nrows - 1)
  561.             break;
  562.         if (l == win->w_buffer->b_file)
  563.             break;
  564.         }
  565.         while (done > win->w_nrows - 1 && l != currline) {
  566.         done -= plines(win, l);
  567.         l = l->l_next;
  568.         }
  569.         newtopline = l;
  570.  
  571.         /*
  572.          * Now work out how many screen lines we want
  573.          * to scroll the window by. This code assumes
  574.          * that the old value of topline is earlier
  575.          * in the buffer than the new, so check this
  576.          * first.
  577.          */
  578.         if (earlier(win->w_topline, newtopline)) {
  579.         done = cntplines(win, win->w_topline, newtopline);
  580.  
  581.         if (done != 0) {
  582.             s_del(win, 0, (int) done);
  583.         }
  584.         }
  585.  
  586.         win->w_topline = newtopline;
  587.         update_window(win);
  588.     }
  589.     }
  590. }
  591.  
  592. /*
  593.  * Make sure the cursor is within the given window.
  594.  *
  595.  * This is needed for commands like control-E & control-Y.
  596.  */
  597. void
  598. move_cursor_to_window(win)
  599. Xviwin        *win;
  600. {
  601.     Posn    *cp;
  602.  
  603.     cp = win->w_cursor;
  604.  
  605.     if (earlier(cp->p_line, win->w_topline)) {
  606.     cp->p_line = win->w_topline;
  607.     coladvance(win, win->w_curswant);
  608.     } else if (!earlier(cp->p_line, win->w_botline)
  609.            && earlier(win->w_topline, win->w_botline)) {
  610.     cp->p_line = win->w_botline->l_prev;
  611.     coladvance(win, win->w_curswant);
  612.     }
  613.     win->w_curs_new = TRUE;
  614. }
  615.